package timing.ukulele.third.service.weixin;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpUserService;
import me.chanjar.weixin.mp.api.WxMpUserTagService;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.bean.result.WxMpUserList;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import timing.ukulele.common.dictionary.YesOrNoEnum;
import timing.ukulele.data.third.data.weixin.WxUserDTO;
import timing.ukulele.data.third.view.weixin.WxUserVO;
import timing.ukulele.third.mapper.wexin.WxUserMapper;
import timing.ukulele.third.persistent.weixin.WxUser;
import timing.ukulele.third.config.mp.WxMpConfiguration;
import timing.ukulele.third.constant.WxConstant;
import timing.ukulele.third.convertor.weixin.WxUserConverter;
import timing.ukulele.third.handler.SubscribeHandler;
import timing.ukulele.third.persistent.weixin.WxApp;
import timing.ukulele.third.service.common.BaseOperatorService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 微信用户
 */
@Slf4j
@Service
@AllArgsConstructor
public class WxUserService extends BaseOperatorService<WxUserMapper, WxUser> {

	private final WxAppService wxAppService;

    public IPage<WxUserVO> pageList(WxUserDTO wxUserDTO) {
        Page pageParam = new Page(wxUserDTO.getCurrent(), wxUserDTO.getPageSize());
        Page page = this.lambdaQuery()
                .eq(WxUser::getAppId, wxUserDTO.getAppId())
                .eq(WxUser::getAppType, wxUserDTO.getAppType())
                .like(StrUtil.isNotBlank(wxUserDTO.getTagId()), WxUser::getTagIdList, wxUserDTO.getTagId())
                .like(StrUtil.isNotBlank(wxUserDTO.getNickName()), WxUser::getNickName, wxUserDTO.getNickName())
                .page(pageParam);
        List<WxUserVO> resultList = WxUserConverter.INSTANCE.p2vList(page.getRecords());
        page.setRecords(resultList);
        return page;
    }

    public WxUserVO getDetails(Long id) {
        WxUser wxUser = this.getById(id);
        return WxUserConverter.INSTANCE.p2v(wxUser);
    }

	public WxUser getByOpenId(String appId, String openId) {
        return this.lambdaQuery()
                .eq(WxUser::getDeleted, 0)
                .eq(WxUser::getAppId, appId)
                .eq(WxUser::getOpenId,openId).one();
	}

    public void synchroWxUser(String appId) throws WxErrorException {
        // 先将已关注的用户取关
        WxUser wxUser = new WxUser();
        wxUser.setSubscribe(WxConstant.SUBSCRIBE_TYPE_NO);

        this.lambdaUpdate()
                .eq(WxUser::getAppId, appId)
                .eq(WxUser::getSubscribe, YesOrNoEnum.YES.getValue().toString())
                .set(WxUser::getSubscribe, YesOrNoEnum.NO.getValue().toString())
                .update();

        WxMpUserService wxMpUserService = WxMpConfiguration.getMpServiceByAppId(appId).getUserService();
        WxApp wxApp = wxAppService.getById(appId);
        this.recursionGet(wxApp, wxMpUserService,null);
    }

    /**
     * 递归获取
     * @param nextOpenid
     */
    void recursionGet(WxApp wxApp,WxMpUserService wxMpUserService,String nextOpenid) throws WxErrorException {
        WxMpUserList userList = wxMpUserService.userList(nextOpenid);
        List<WxUser> listWxUser = new ArrayList<>();
        List<WxMpUser> listWxMpUser = getWxMpUserList(wxMpUserService,userList.getOpenids());
        listWxMpUser.forEach(wxMpUser -> {
            WxUser wxUser = this.getByOpenId(wxApp.getAppId(), wxMpUser.getOpenId());
            // 用户未存在
            if(wxUser == null){
                wxUser = new WxUser();
                wxUser.setSubscribeNum(1);
            }
            SubscribeHandler.setWxUserValue(wxApp,wxUser,wxMpUser);
            listWxUser.add(wxUser);
        });
        this.saveOrUpdateBatch(listWxUser);
        if(userList.getCount() >= 10000){
            this.recursionGet(wxApp,wxMpUserService,userList.getNextOpenid());
        }
    }

    /**
     * 分批次获取微信粉丝信息 每批100条
     * @param wxMpUserService
     * @param openidsList
     * @return
     * @throws WxErrorException
     * @author
     */
    private List<WxMpUser> getWxMpUserList(WxMpUserService wxMpUserService, List<String> openidsList) throws WxErrorException {
        // 粉丝openid数量
        int count = openidsList.size();
        if (count <= 0) {
            return new ArrayList<>();
        }
        List<WxMpUser> list = Lists.newArrayList();
        List<WxMpUser> followersInfoList;
        int a = count % 100 > 0 ? count / 100 + 1 : count / 100;
        for (int i = 0; i < a; i++) {
            if (i + 1 < a) {
                log.info("i:{},from:{},to:{}", i, i * 100, (i + 1) * 100);
                followersInfoList = wxMpUserService.userInfoList(openidsList.subList(i * 100, ((i + 1) * 100)));
                if (null != followersInfoList && !followersInfoList.isEmpty()) {
                    list.addAll(followersInfoList);
                }
            }
            else {
                log.info("i:{},from:{},to:{}", i, i * 100, count - i * 100);
                followersInfoList = wxMpUserService.userInfoList(openidsList.subList(i * 100, count));
                if (null != followersInfoList && !followersInfoList.isEmpty()) {
                    list.addAll(followersInfoList);
                }
            }
        }
        log.info("本批次获取微信粉丝数：",list.size());
        return list;
    }

    public Boolean updateRemark(WxUserDTO wxUserDTO) throws WxErrorException {

        // 修改本地数据
        WxUser wxUser = this.getById(wxUserDTO.getId());
        wxUser.setRemark(wxUserDTO.getRemark());
        this.updateById(wxUser);
        // 修改微信数据
        WxMpUserService wxMpUserService = WxMpConfiguration.getMpServiceByAppId(wxUser.getAppId()).getUserService();
        wxMpUserService.userUpdateRemark(wxUser.getOpenId(), wxUserDTO.getRemark());

        return Boolean.TRUE;
    }

    @Transactional(rollbackFor = Exception.class)
    public void setTag(WxUserDTO wxUserDTO) throws WxErrorException {
        List<WxUser> wxUserList = this.lambdaQuery()
                .in(WxUser::getId, wxUserDTO.getIds())
                .list();
        for (Long tagId : wxUserDTO.getTagIds()) {
            this.tagging(wxUserDTO.getTaggingType(), wxUserDTO.getAppId(), tagId, wxUserList);
        }
    }

    private void tagging(Integer taggingType, String appId, Long tagId, List<WxUser> wxUserList) throws WxErrorException {

        List<String> openIds = wxUserList.stream().map(item -> item.getOpenId()).collect(Collectors.toList());

        WxMpUserTagService wxMpUserTagService = WxMpConfiguration.getMpServiceByAppId(appId).getUserTagService();
        if(YesOrNoEnum.YES.getValue().equals(taggingType)){
            for(WxUser wxUser : wxUserList){
                Long[] tagIdList = wxUser.getTagIdList();
                List<Long> list = Arrays.asList(tagIdList);
                list = new ArrayList<>(list);
                if(!list.contains(tagId)){
                    list.add(tagId);
                    tagIdList = list.toArray(new Long[list.size()]);
                    wxUser.setTagIdList(tagIdList);
                    this.updateById(wxUser);
                }
            }
            wxMpUserTagService.batchTagging(tagId, (String[]) openIds.toArray());
        }
        if(YesOrNoEnum.NO.getValue().equals(taggingType)){
            for(WxUser wxUser : wxUserList){
                Long[] tagIdList = wxUser.getTagIdList();
                List<Long> list = Arrays.asList(tagIdList);
                list = new ArrayList<>(list);
                if(list.contains(tagId)){
                    list.remove(tagId);
                    tagIdList = list.toArray(new Long[list.size()]);
                    wxUser.setTagIdList(tagIdList);
                    this.updateById(wxUser);
                }
            }
            wxMpUserTagService.batchUntagging(tagId, (String[]) openIds.toArray());
        }
    }

}
